#include "common_impl.h"
#include <stdio.h>
#include <stdlib.h>

/* variables globales */

/* un tableau gerant les infos d'identification */
/* des processus dsm */
dsm_proc_t *proc_array = NULL; 

/* le nombre de processus effectivement crees */
volatile int num_procs_creat = 0;

void usage(void)
{
	fprintf(stdout,"Usage : dsmexec machine_file executable arg1 arg2 ...\n");
	fflush(stdout);
	exit(EXIT_FAILURE);
}

void sigchld_handler(int sig)
{
	/* on traite les fils qui se terminent */
	/* pour eviter les zombies */
}


int main(int argc, char *argv[])
{
	if (argc < 3){
		usage();
	} else {
		//int num_procs = 0;
		int i;
		int port_num;
		int status;

		printf("%s\n", argv[1]);



		typedef int pipe_t[2];

		char* buffer = malloc(MAXBUF);
		char* buffer2 = malloc(MAXBUF);
		int k=0;
		int tr = 1;
		int ch;
		int count=1;
		int j=3;
		pid_t pid;

		/* Mise en place d'un traitant pour recuperer les fils zombies*/
		/* XXX.sa_handler = sigchld_handler; */

		/* lecture du fichier de machines */
		/* 1- on recupere le nombre de processus a lancer */

		FILE *fp = fopen(argv[1],"r");
		if (fp == NULL){
			perror("fopen");
			exit(EXIT_FAILURE);
		}


		do
		{
			ch = fgetc(fp);
			if( ch == '\n') count++;
		} while( ch != EOF );

		printf("Total number of machines %d\n",count);
		/* 2- on recupere les noms des machines : le nom de */
		/* la machine est un des elements d'identification */

		typedef struct{
			char str[20];
			pid_t pid_child;
		} str_t;

		pipe_t* fdout = malloc(count*sizeof(pipe_t));
		pipe_t* fderr = malloc(count*sizeof(pipe_t));


		str_t* tab = malloc(count*sizeof(str_t));



		char** newargv = malloc((argc+1)*sizeof(char*));
		newargv[0] = malloc(sizeof(char)*20);
		strcpy(newargv[0], "ssh");
		newargv[1] = malloc(sizeof(char)*20);
		//strcpy(newargv[1], "karim@karim-U");
		strcpy(newargv[1], "julien-1215B");
		newargv[2] = malloc(sizeof(char)*20);
		strcpy(newargv[2], "./dsmwrap");
		for (j=3; j< argc+1; j++){
			newargv[j] = malloc(sizeof(char)*20);
			strcpy(newargv[j], argv[j-1]);
			printf("%s\n", newargv[j]);
		}
		newargv[j] = NULL;

		rewind(fp);

		for (i=0; i<count; i++)
		{
			fgets(tab[i].str, 20, fp);
			supprime_retour(tab[i].str);
		}


		struct sockaddr_in servaddr; // socket address struct
		/* creation de la socket d'ecoute */
		int fd = creer_socket(1, &port_num);

		/* + ecoute effective */


		/*Set all bytes in socket address structure to zero,
			    and fill in the relevant data members */

		memset(&servaddr, 0, sizeof(servaddr));
		servaddr.sin_family      = AF_INET;
		servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
		servaddr.sin_port        = htons(port_num);



		/* Bind our socket addresss to the listening socket,
			     and call listen()*/

		if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &tr, sizeof(int)) == -1) {
			perror("setsockopt");
			exit(1);
		}
		if ( bind(fd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0 ) {
			fprintf(stderr, "ECHOSERV: Error calling bind()\n");
			exit(EXIT_FAILURE);
		}

		if ( listen(fd, LISTENQ) < 0 ) {
			fprintf(stderr, "ECHOSERV: Error calling listen()\n");
			exit(EXIT_FAILURE);
		}
		printf("Attente de connexion\n");

		/*Initialisation du nombre de processeurs*/

		// dynamique en fct des nombre de lignes.

		/* creation des fils */
		for(i = 0; i < count ; i++) {

			/* creation du tube pour rediriger stdout */
			if((pipe(fdout[i])) == -1) {
				perror("pipe");
				exit(1);

			}
			/* creation du tube pour rediriger stderr */
			if((pipe(fderr[i])) == -1) {
				perror("pipe");
				exit(1);
			}

			pid = fork();
			if(pid == -1) ERROR_EXIT("fork");

			if (0 == pid) { /* fils */
				//pid_t machin = getpid();
				/* redirection stdout */
				close(fdout[i][0]);
				dup2(fdout[i][1],fileno(stdout));
				close(fdout[i][1]);

				/* redirection stderr */
				close(fderr[i][0]);
				dup2(fderr[i][1],fileno(stderr));
				close(fderr[i][1]);

				/* Creation du tableau d'arguments pour le ssh */

				/* jump to new prog : */
				/* execvp("ssh",newargv); */
				execvp("ssh", newargv);
				//execl("/usr/bin/ssh","ssh","karim@karim-U","./truc",(char *)0);

			} else  if(pid > 0) { /* pere */
				/* fermeture des extremites des tubes non utiles */
				//dup2(fdout[i][0],fileno(stdin));
				//close(fdout[i][0]);
				close(fdout[i][1]);
				//dup2(fderr[i][0],fileno(stderr));
				//close(fderr[i][0]);
				close(fderr[i][1]);
				//printf("I'm father before wait\n");
				//waitpid(pid, &status, 0);
				//printf("I'm father after wait\n");
				//printf("The output of the child is : \n");


				num_procs_creat++;
			}
		}



		clt_t* machine = malloc(count * sizeof(clt_t));

		for (i=0; i<count; i++){
			machine[i].buffer3 = malloc(MAXBUF);
		}
		int size;
		char glob_info[MAXBUF];

		for(i = 0; i < num_procs_creat ; i++){

			/* on accepte les connexions des processus dsm */
			size = sizeof (servaddr);
			machine[i].fd_com = accept (fd, (struct sockaddr *) &servaddr,
					(socklen_t *) &size);
			if (machine[i].fd_com  < 0)
			{
				perror ("accept");
				exit (EXIT_FAILURE);
			}
			memset(&buffer[0], 0, sizeof(buffer)+1);
			if( Readline(machine[i].fd_com, buffer, MAXBUF-1) < 0) // Lire la réponse du serveur : Quel est votre pseudo ?
			{
				puts("Read failed");
				return 1;
			}
			printf("Client state1: %s", buffer);

			if( Readline(machine[i].fd_com, buffer, MAXBUF-1) < 0) // Lire la réponse du serveur : Quel est votre pseudo ?
			{
				puts("Read failed");
				return 1;
			}
			printf("Client state2: %s", buffer);

			/*  On recupere le nom de la machine distante */
			fprintf (stdout,"Server: connect number %d, from host %s, port %d.\n",
					i, inet_ntoa(servaddr.sin_addr),
					(int) ntohs(servaddr.sin_port));
			if( Readline(machine[i].fd_com, buffer2, MAXBUF-1) < 0)
			{
				puts("Read failed");
				return 1;
			}
			printf("Client state3: %s", buffer2);
			supprime_retour(buffer2);
			supprime_retour(buffer);
			sprintf(machine[i].buffer3, "%d:%d:%s:%s", i+1, count, buffer2, buffer);
			printf("Info to broadcast %s\n", machine[i].buffer3);
			// RANG:NUM:IP:PORT:PID
			//supprime_retour(machine[i].buffer3);
			//strcat(bufferx, machine[i].buffer3);


			/* 1- d'abord la taille de la chaine */
			/* 2- puis la chaine elle-meme */

			/* On recupere le pid du processus distant  */
			/* On recupere le numero de port de la socket */
			/* d'ecoute des processus distants */
		}

		/* envoi du nombre de processus aux processus dsm*/

		/* envoi des rangs aux processus dsm */
		//strcat(bufferx, "\n");
		for (j = 0; j < count; j++){
			strcat(glob_info, machine[j].buffer3);

			if (j != count-1) {
				strcat(glob_info, "-");
			} else {
				strcat(glob_info, "\n");
			}

			strcat(machine[j].buffer3, "\n");
			if( Sendline(machine[j].fd_com, machine[j].buffer3,
					strlen(machine[j].buffer3)) < 0) {
				puts("Send failed");
				return 1;
			}

		}
		printf("globale info: %s", glob_info);
		for (j = 0; j < count; j++){
			if( Sendline(machine[j].fd_com, glob_info,
					strlen(glob_info)) < 0) {
				puts("Send failed");
				return 1;
			}

		}
		fprintf(stdout, "ok\n");



		/*for (k = j+1; k < count; k++){
			if (machine[k].buffer3[strlen(machine[k].buffer3)-1] != '\n'){
				strcat(machine[k].buffer3, "\n");
			}
			if( Sendline(machine[j].fd_com, machine[k].buffer3,
					strlen(machine[k].buffer3)) < 0) {
				puts("Send failed");
				return 1;
			}
		}*/


		/* envoi des infos de connexion aux processus */

		/* gestion des E/S : on recupere les caracteres */
		/* sur les tubes de redirection de stdout/stderr */

		/*

		 */

		fd_set active_fd_set, tmpfds;

		int max = 0;

		FD_ZERO (&active_fd_set);
		for (i=0; i<num_procs_creat; i++){
			FD_SET (fdout[i][0], &active_fd_set);
			max = max > fdout[i][0] ? max : fdout[i][0];
			FD_SET (fderr[i][0], &active_fd_set);
			max = max > fderr[i][0] ? max : fderr[i][0];
		}

		while(1)
		{
			/*je recupere les infos sur les tubes de redirection
            jusqu'à ce qu'ils soient inactifs (ie fermes par les
            processus dsm ecrivains de l'autre cote ...)*/
			tmpfds = active_fd_set;

			if (select(max+1, &tmpfds, NULL, NULL, NULL) < 0)
			{
				perror ("select");
				exit (EXIT_FAILURE);
			}

			int i=0;
			for (i = 0; i <= max; ++i) {
				if (FD_ISSET (i, &tmpfds)) {
					memset(buffer, 0, sizeof(buffer));

					if( Readline(i, buffer, MAXBUF-1) < 0)
					{
						puts("Read failed");
						return 1;
					}
					supprime_retour(buffer);

					if (strlen(buffer) > 0){
						fprintf(stdout,"[Proc %d : nom_machine %s : stdout :%s]\n", i, tab[i].str, buffer);
					}
				}

			}
		}


		/* on attend les processus fils */

		/* on ferme les descripteurs proprement */

		/* on ferme la socket d'ecoute */
		free(tab);
	}

	return 0;
}

